//! 本库为Rust Tiny Orm的辅助宏

extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

/// 为结构体自动实现TinyOrmDbModel数据库模型
///
/// # 用法
///
/// #[derive(TinyOrm)]
///
/// 本宏定义了如下属性，用于设置数据映射接口和参数
///
/// # 应用于结构体的属性
///
/// 1. #[orm_table_name = "core_user"]
///
/// 可选属性，可设置多个。
///
/// 自定义数据库表名称。
/// 未设置则表名称为结构名称的蛇形命名，
/// 例如：TestUser转换为test_user。
///
/// 2. #[orm_table_name_pref = "core"]
///
/// 可选属性，可设置多个。
///
/// 为自动生成的数据库表名称增加前缀。
/// 例如：TestUser转换为core_test_user.
///
/// 3. #[orm_query]
///
/// 可选属性，可设置多个。
///
/// 生成Self::orm_query_with开头的获取多条记录的查询方法。
///
/// 有三个子属性：
///
/// * name
///    * 生成的函数名称后缀
///    * 如果name设置为tel,实际生成的方法名称为：orm_query_with_tel
/// * sql_where
///    * 数据库select语句的where部分
///    * 可选，未设置则为获取所有记录
/// * args
///    * 生成方法的参数部分定义
///    * 参数数量应与sql_where定义的?参数数量一致
///    * 与实际函数定义的写法一致，如定义2个参数，格式为：arg1:type1,arg2:type2
///    * 实际生成的函数，会自动添加pool: &TinyOrmDbPool
/// * doc 
///    * 生成方法的文档说明
///
/// 例如：
///
/// ```ignore
/// #[orm_query(
///    name = "name_and_tel",
///    sql_where = "user.name = ? and mobile_phone = ?",
///    args = "name:&str,mobile_phone:&str",
///    doc = "根据姓名和手机号查询用户",
/// )]
/// 自动生成如下函数:
/// /// 根据姓名和手机号查询用户
/// pub async fn orm_query_with_name_and_tel(pool: &TinyOrmDbPool, name:&str,mobile_phone:&str) ->AnyhowResult<Vec<Self>>{...}
/// ```
///
/// 4. #[orm_update]
///
/// 可选属性，可设置多个。
///
/// 生成Self::orm_update_with开头的将更新记录方法。
///
/// 有三个子属性：
///
/// * name
///    * 生成的函数名称后缀
///    * 如果name设置为tel,实际生成的方法名称为：orm_update_with_tel
/// * sql_set
///    * 数据库update语句的set部分
/// * sql_where
///    * 数据库update语句的where部分
///    * 可选，未设置则为更新所有记录
/// * args
///    * 生成方法的参数部分定义
///    * 与实际函数定义的写法一致，如定义2个参数，格式为：arg1:type1,arg2:type2
///    * 参数数量应与sql_set+sql_where定义的?参数数量一致
///    * 实际生成的函数，会自动添加pool: &TinyOrmDbPool
/// * doc 
///    * 生成方法的文档说明
///
/// 例如：
///
/// ```ignore
/// #[orm_update(
///    name = "name_and_tel",
///    sql_set = "user.name = ? ,user.mobile_phone = ?",
///    sql_where = "id = ?",
///    args = "id:u32,name:&str,mobile_phone:&str",
///    doc = "根据id更新姓名和手机号",
/// )]
/// 自动生成如下更新函数:
/// /// 根据id更新姓名和手机号
/// pub async orm_update_with_name_and_tel(pool: &TinyOrmDbPool, name: &str,mobile_phone: &str) -> AnyhowResult<()>{...}
/// ```
///
/// 5. #[orm_delete]
///
/// 可选属性，可设置多个。
///
/// 生成Self::orm_delete_with开头的删除记录方法。
///
///
/// 有三个子属性：
///
/// * name
///    * 生成的函数名称后缀
///    * 如果name设置为tel,实际生成的方法名称为：orm_delete_with_tel
/// * sql_where
///    * 数据库delete语句的where部分
///    * 可选，未设置则为删除所有记录
/// * args
///    * 生成方法的参数部分定义
///    * 参数数量应与sql_where定义的?参数数量一致
///    * 与实际函数定义的写法一致，如定义2个参数，格式为：arg1:type1,arg2:type2
///    * 实际生成的函数，会自动添加pool: &TinyOrmDbPool
/// * doc 
///    * 生成方法的文档说明
///
/// 例如：
///
/// ```ignore
/// #[orm_delete(
///    name = "name_and_tel",
///    sql_where = "user.name = ? and mobile_phone = ?"
///    args = "name:&str,mobile_phone:&str",
///    doc = "根据姓名和手机号删除用户"
/// )]
/// 自动生成如下函数:
/// /// 根据姓名和手机号查询用户
/// pub async fn orm_delete_with_name_and_tel(pool: &TinyOrmDbPool, name:&str,mobile_phone:&str) ->AnyhowResult<()>{...}
/// ```
///
/// 6. #[orm_exist]
///
/// 可选属性，可设置多个。
///
/// 生成Self::orm_exist_with开头的删除记录方法。
///
///
/// 有三个子属性：
///
/// * name
///    * 生成的函数名称后缀
///    * 如果name设置为tel,实际生成的方法名称为：orm_exist_with_tel
/// * sql_where
///    * 数据库select语句的where部分
///       * 实际生成的sql为：select count(1) from table_name where sql_where
///    * 可选，未设置则为所有记录
/// * args
///    * 生成方法的参数部分定义
///    * 参数数量应与sql_where定义的?参数数量一致
///    * 与实际函数定义的写法一致，如定义2个参数，格式为：arg1:type1,arg2:type2
///    * 实际生成的函数，会自动添加pool: &TinyOrmDbPool
/// * doc 
///    * 生成方法的文档说明
///
/// 例如：
///
/// ```ignore
/// #[orm_exist(
///    name = "id",
///    sql_where = "id = ?"
///    args = "id:u32",
///    doc = "根据id查询记录是否存在"
/// )]
/// 自动生成如下函数:
/// /// 根据id查询记录是否存在
/// pub async fn orm_exist_with_name_and_tel(pool: &TinyOrmDbPool, id: u32) ->AnyhowResult<bool>
/// ```
///
/// # 应用于结构体字段的方法
///
/// 1. #[orm_pk(name="id", auto="true")]
///
/// 至少需要设置1个，可设置多个。
///
/// 设置当前字段为主键字段
///
/// 有两个子属性：
///
/// * name
///    * 设置对应数据库表中的字段名称
///    * 可选属性，如果未设置，数据库中的字段名称和结构体字段名称一致
/// * auto
///    * 设置为true，则标识这是个自动增长的主键，在插入时不设置值
///    * 可选属性
///
/// 该属性会自动生成如下方法：
///
/// 多个主键会自动汇聚为一个方法，并将主键设置为对应的函数方法的参数
///
/// * Self::orm_get_with_pk
///    * 通过主键获取单条记录
///    * 如果设置了多个主键，会将按照字段定义顺序，合并到该方法的参数中
///    * 函数定义
///       * pub async fn orm_get_with_pk(pool: &TinyOrmDbPool,主键字段1:字段类型1,主键字段2:字段类型2,...) ->  AnyhowResult<Self>
///       * 结构体字段类型自动转换
///           * Option<T> 自动转换为 T
///           * String 自动转换为 &str
/// * Self::orm_delete_with_pk
///    * 通过主键删除记录
///    * 如果设置了多个主键，会将按照字段定义顺序，合并到该方法的参数中
///    * 函数定义
///       * pub async fn orm_delete_with_pk(pool: &TinyOrmDbPool,主键字段1:字段类型1,主键字段2:字段类型2,...) ->  AnyhowResult<()>
///       * 结构体字段类型自动转换
///           * Option<T> 自动转换为 T
///           * String 自动转换为 &str
/// * self::orm_delete
///    * 通过主键删除当前实例在数据库记录
///    * 如果设置了多个主键，会将按照字段定义顺序，合并到该方法的参数中
///    * 函数定义
///       * pub async fn orm_delete(&self,pool: &TinyOrmDbPool) ->  AnyhowResult<Self>
/// * Self::orm_exist_with_pk
///    * 通过主键查询记录是否存在
///    * 如果设置了多个主键，会将按照字段定义顺序，合并到该方法的参数中
///    * 函数定义
///       * pub async fn orm_exists_with_pk(pool: &TinyOrmDbPool,主键字段1:字段类型1,主键字段2:字段类型2,...) ->  AnyhowResult<bool>
///       * 结构体字段类型自动转换
///           * Option<T> 自动转换为 T
///           * String 自动转换为 &str
/// * Self::orm_exist
///    * 通过主键查询当前实例在数据库记录是否存在
///    * 如果设置了多个主键，会将按照字段定义顺序，合并到该方法的参数中
///    * 函数定义
///       * pub async fn orm_exists_with_pk(&self,pool: &TinyOrmDbPool) ->  AnyhowResult<bool>
///
/// # 示例
///
/// ```ignore
/// struct TestUser {
///   #[orm_pk(name="user_id",auto="true")]
///   id:u32,
///   #[orm_pk]
///   tel:String,
/// }
///
/// //以上会自动生成如下方法
/// pub async fn orm_get_with_pk(pool: &TinyOrmDbPool,id:u32,tel:&str) ->  AnyhowResult<Self>{...}
/// pub async fn orm_delete_with_pk(pool: &TinyOrmDbPool,id:u32,tel:&str) ->  AnyhowResult<()>{...}
/// pub async fn orm_exist_with_pk(pool: &TinyOrmDbPool,id:u32,tel:&str) ->  AnyhowResult<bool>{...}
/// pub async fn orm_delete(self,pool: &TinyOrmDbPool) ->  AnyhowResult<bool>{...}
/// pub async fn orm_exist(self,pool: &TinyOrmDbPool) ->  AnyhowResult<bool>{...}
///
/// ```
///
/// 2. #[orm_join]
///
/// 可选属性，可设置多个。
///
/// 设置对应字段为关联其他表的join字段
///
/// 有六个子属性：
///
/// * name
///    * 设置对应数据库表中的字段名称
///    * 可选属性，如果未设置，数据库中的字段名称和结构体字段名称一致
/// * select_field
///    * 从join表select数据库字段的对应sql语句
///    * 该语句会与结构体自身对应数据库表的字段合并为select的选择字段清单
/// * join
///    * 连接表的join语句
/// * link_id
///    * 字段的rust类型的对应连接字段
///    * 例如，如某结构体User
///       * 其中某一字段定义为：user_type:UserType,设置link_id="id"
///       * 写入User表记录时，会自动读取self.user_type.id为对应数据库字段的值
/// * link_id_type
///    * 连接字段的rust类型
///    * 如以上UserType::id为u32类型，则link_id_type="u32"
/// * skip_method
///    * 是否不自动生成相关join方法
///    * true，则不自动生成
///
/// 如为设置skip_method="true"，该属性会自动生成如下方法：
///
/// * Self::orm_query_join_with_字段名称
///    * 使用join字段查询记录
///    * 函数定义
///       * pub async fn orm_query_join_with_字段名称(pool: &TinyOrmDbPool,字段名称:字段类型) -> AnyhowResult<Vec<Self>>
///       * 结构体字段类型自动转换
///           * Option<T> 自动转换为 T
///           * String 自动转换为 &str
/// * Self::orm_query_join_with_字段名称_{link_id名称}
///    * 使用join字段的link_id查询记录
///    * 函数定义
///       * pub async fn orm_query_join_with_字段名称_{link_id名称}(pool: &TinyOrmDbPool,link_id名称:link_id_type名称) -> AnyhowResult<Vec<Self>>
/// * Self::orm_delete_join_with_字段名称
///    * 使用join字段删除记录
///    * 函数定义
///       * pub async fn orm_delete_join_with_字段名称(pool: &TinyOrmDbPool,字段名称:字段类型) -> AnyhowResult<()>
///       * 结构体字段类型自动转换
///           * Option<T> 自动转换为 T
///           * String 自动转换为 &str
/// * Self::orm_delete_join_with_字段名称_{link_id名称}
///    * 使用join字段的link_id删除记录
///    * 函数定义
///       * pub async fn orm_delete_join_with_字段名称_{link_id名称}(pool: &TinyOrmDbPool,link_id名称:link_id_type名称) -> AnyhowResult<()>
/// * self::orm_update_join_with_字段名称
///    * 使用当前实例的对应join字段.link_id保存到数据库中对应字段中
///    * 函数定义
///       * pub async fn orm_update_join_with_字段名称(&self,pool: &TinyOrmDbPool) -> AnyhowResult<()>
///
/// # 示例
///
/// ```
/// struct User {
///    #[orm_join(
///       name="user_type_id", 
///       select_field="user_type.name as user_type_name, user_type.template",
///       join="JOIN user_type ON user_type.id = user_type_id",
///       link_id="id",
///       link_id_type="u32",
///    )]
///    user_type:UserType
/// }
/// // 会自动生成如下方法
/// pub async fn orm_query_join_with_user_type(pool: &TinyOrmDbPool,user_type:UserType) -> AnyhowResult<Vec<Self>>{...}
/// pub async fn orm_query_join_with_user_type_id(pool: &TinyOrmDbPool,query_value:u32) -> AnyhowResult<Vec<Self>>{...}
/// pub async fn orm_delete_join_with_user_type(pool: &TinyOrmDbPool,user_type:UserType) -> AnyhowResult<()>{...}
/// pub async fn orm_delete_join_with_user_type_id(pool: &TinyOrmDbPool,query_value:u32) -> AnyhowResult<()>{...}
/// pub async fn orm_update_join_with_user_type(&self,pool: &TinyOrmDbPool) -> AnyhowResult<()>{...}
/// ```
///
/// 3. #[orm_field(name = "custom_table_field_name")]
///
/// 可选属性，可设置多个。
///
/// 设置对应数据库表的字段名称
///
/// 有1个子属性：
///
/// * name
///    * 设置对应数据库表中的字段名称
///    * 可选属性，如果未设置，数据库中的字段名称和结构体字段名称一致
///
/// 4. #[orm_ignore]
///
/// 可选属性，可设置多个。
///
/// 默认情况下，结构体的每个字段均对应一个数据库表字段。
/// 设置该属性后，可忽略对应字段，不对应数据库表的字段名称
///
/// 5. #[orm_update]
///
/// 生成更新数据库中的对应字段值的方法，方法定义如下:
/// pub async fn orm_update_字段名称(&self,pool: &TinyOrmDbPool) ->AnyhowResult<()>
///
/// 6. #[orm_query]
///
/// 生成使用该字段查询数据库记录的方法，方法定义如下:
/// * pub async fn orm_query_with_字段名称(pool: &TinyOrmDbPool,query_value:字段类型) -> AnyhowResult<Vec<Self>>
/// * 结构体字段类型自动转换
///    * Option<T> 自动转换为 T
///    * String 自动转换为 &str
///
/// 7. #[orm_get]
///
/// 生成使用该字段获取单条数据库记录的方法，方法定义如下:
/// * pub async fn orm_get_with_字段名称(pool: &TinyOrmDbPool,query_value:字段类型) -> AnyhowResult<Self>
/// * 结构体字段类型自动转换
///    * Option<T> 自动转换为 T
///    * String 自动转换为 &str
///
/// # 其他自动生成方法
///
/// 1. self.orm_insert方法
///
/// 将当前结构体实例数据插入到数据库表中，用于新增记录的保存
///
/// 方法定义如下：
/// pub async fn orm_insert(&mut self, pool: &TinyOrmDbPool) -> AnyhowResult<()>
///
/// 2. self.orm_update_all方法
///
/// 将当前结构体实例数据更新到数据库表中，用于变更记录的保存
///
/// 方法定义如下：
/// pub async fn orm_update_all(&self,pool: &TinyOrmDbPool) -> AnyhowResult<()>
#[proc_macro_derive(
    TinyOrm,
    attributes(
        orm_table_name,
        orm_table_name_pref,
        orm_query,// 定义查询方法
        orm_pk,// 标识主键，定义组键方法
        orm_delete,// 定义删除方法
        orm_update,// 定义update方法
        orm_exist,// 定义exist方法
        orm_field,// 数据库字段重命名
        orm_join,// join表定义
        orm_ignore,// 忽略字段，不对应数据库表字段
        orm_get,// 获取单条记录
    )
)]
pub fn tiny_orm_derive(input: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(input as DeriveInput);
    trait_tool::impl_tiny_orm(&ast)
}

/// 自动实现TinyOrmQuery trait宏
///
/// 自动实现相关数据库的插入、更新、查询、删除等接口
///
/// 用法如下：
/// #[derive(TinyOrmQuery)]
#[proc_macro_derive(TinyOrmQuery)]
pub fn db_query_trait_macro_derive(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    impl_db_query_trait_macro(&ast)
}
/// 为请求报文自动实现DbQuery
fn impl_db_query_trait_macro(ast: &syn::DeriveInput) -> TokenStream {
    let name = &ast.ident;
    let gen = quote! {
        #[async_trait]
        impl TinyOrmDbQuery for #name {}
    };
    gen.into()
}

mod trait_tool;
